#!/usr/bin/env python3
#coding=utf-8

import threading 
import socket
import select
import sys
import time 
from typing import List


class Client:
    """TCP客户端，有SOCK和地址，其中地址为两个成员的List,IP+PORT
    """
    def __init__(self, _sock, _addr):
        self.sock = _sock 
        self.addr = _addr   
        
    def info(self):
        return "%s:%d" % (self.addr[0], self.addr[1])  


class TcpServer:
    def __init__(self, ip="0.0.0.0", port=3210):
        self._ip = ip 
        self._port = port 
        self._thread = threading.Thread(target=self.__thread_main)
        self._running = False 
        self._clients = [] 
        self._fds = []
        self._sock = None

    def info(self):
        return "%s:%d" % (self._ip, self._port)

    def is_running(self)->bool:
        return self._running

    def clients_num(self)->int:
        return len(self._clients)
        
    def open_socket(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)   
        s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True)
        s.ioctl(socket.SIO_KEEPALIVE_VALS, (1, 60 * 1000, 30 * 1000))

        try:
            s.bind((self._ip, self._port))
        except Exception as e:
            s.close()
            return None 
        s.listen(10)
        return s 

    def close_client(self, sock):
        #关闭
        sock.close()
        # 从客户端中移除
        self._fds.remove(sock)            
        # 从监听列表中移除
        for c in self._clients:
            if c.sock is sock:
                self._clients.remove(c)

    def start(self)->bool:
        if self._running:
            return False 
        self._sock = self.open_socket()
        if not self._sock:
            return False 
        self._thread.start()
        return True 
    
    def stop(self):
        if self._running:
            self._running = False 
            self._thread.join()

    def get_client(self, sock)->Client:
        for c in self._clients:
            if c.sock == sock:
                return c
        return None

    def add_client(self, client:Client):
        """
        添加一个客户端到接收列表中
        """
        self._fds.append(client.sock)
        self._clients.append(client)
        return client

    def remove_client(self, client:Client):
        """
        移除指定的客户端
        """
        #关闭
        client.sock.close()
        # 从客户端中移除
        self._fds.remove(client.sock)            
        # 从监听列表中移除
        for c in self._clients:
            if c.sock is client.sock:
                self._clients.remove(c)

    def on_client_connect(self, client:Client):
        self.add_client(client)
        print("connection(%s) setup, total:%d" % (client.info(), len(self._clients))) 
    
    def on_client_disconnect(self, client):
        self.remove_client(client)
        print("connection(%s) close, total:%d" % (client.info(), len(self._clients))) 
    
    def on_receive(self, client:Client, data:bytes):
        print("rx data from (%s): %s" % (client.info(), str(data)))

    def on_start(self):
        pass

    def on_stop(self):
        pass

    def broadcast(self, data:bytes):
        for c in self._clients:
            try:
                c.sock.send(data)
            except Exception as e:
                pass 

    def __thread_main(self):

        self._running = True 
        self._fds.append(self._sock)

        self.on_start()

        while self._running:
            rfds, wfds, efds = select.select(self._fds, [], [], 0.1)
            for fd in rfds:
                if fd is self._sock:
                    # 有新的连接进来
                    client_sock, client_addr = self._sock.accept()
                    self.on_client_connect(Client(client_sock, client_addr))
                else:
                    # 其他连接的消息
                    client = self.get_client(fd)
                    try:
                        data = fd.recv(1024)
                    except Exception as e:
                        data = None
                    if not data:
                        # 连接中断
                        self.on_client_disconnect(client)
                    else:
                        self.on_receive(client, data)
        
        self._running = False
        ## 关闭所有客户端
        for c in self._clients:
            c.sock.close()
        self._sock.close()
        self._sock = None 
        self.on_stop()

    def test(self):
        print("TCP listen on %s:%d, CTRL+C to cancel" % (self._ip, self._port))
        self.start()
        while True:
            time.sleep(1)
            self.broadcast(b'This is broadcast message\n')
